﻿using System.Data.Objects;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;
using System.Data;

namespace UtfprLibrary.EntityFramework {
    /// <summary>
    /// TODO
    /// </summary>
    public static class ObjectContextExtensions {
        #region Internal

        /// <summary>
        /// TODO
        /// </summary>
        /// <param name="context"></param>
        /// <param name="entityTypeName"></param>
        /// <returns></returns>
        public static string GetEntitySetName(this ObjectContext context, string entityTypeName) {
            var container = context.MetadataWorkspace.GetEntityContainer(
                context.DefaultContainerName, DataSpace.CSpace);

            return (from meta in container.BaseEntitySets
                    where meta.ElementType.Name == entityTypeName
                    select meta.Name).FirstOrDefault();
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <param name="context"></param>
        /// <param name="entityTypeName"></param>
        /// <returns></returns>
        public static string GetEntitySetFullName(this ObjectContext context, string entityTypeName) {
            return context.DefaultContainerName + "." + GetEntitySetName(context, entityTypeName);
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="context"></param>
        /// <returns></returns>
        public static IObjectSet<TEntity> GetObjectSet<TEntity>(this ObjectContext context) where TEntity : class {
            return (IObjectSet<TEntity>)context.GetType().GetProperties()
                .Where(p => p.PropertyType == typeof(IObjectSet<TEntity>))
                .FirstOrDefault().GetValue(context, null);
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="context"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static TEntity GetObjectById<TEntity, TKey>(this ObjectContext context, TKey id)
            where TEntity : class
            where TKey : struct {
            MetadataWorkspace workspace = context.MetadataWorkspace;

            workspace.LoadFromAssembly(Assembly.Load(typeof(TEntity).Assembly.GetName().Name));

            EdmType edmOS = workspace.GetItems<EdmType>(DataSpace.OSpace)
                .Where(type => type.FullName.Equals(typeof(TEntity).FullName))
                .FirstOrDefault();

            EdmMember edmMemberID = ((ReadOnlyMetadataCollection<EdmMember>)edmOS.GetType()
                .BaseType.BaseType.GetProperty("KeyMembers").GetValue(edmOS, null)).FirstOrDefault();

            EntityKey key = new EntityKey(context.GetEntitySetFullName(typeof(TEntity).Name), edmMemberID.Name, id);

            return (TEntity)context.GetObjectByKey(key);
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="context"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static bool IsKeyMember<TEntity>(this ObjectContext context, string propertyName)
            where TEntity : class {

            MetadataWorkspace workspace = context.MetadataWorkspace;

            workspace.LoadFromAssembly(Assembly.Load(typeof(TEntity).Assembly.GetName().Name));

            EdmType edmOS = workspace.GetItems<EdmType>(DataSpace.OSpace)
                .Where(type => type.FullName.Equals(typeof(TEntity).FullName))
                .FirstOrDefault();

            EdmMember edmMemberID = ((ReadOnlyMetadataCollection<EdmMember>)edmOS.GetType()
                .BaseType.BaseType.GetProperty("KeyMembers").GetValue(edmOS, null)).FirstOrDefault();

            if (edmMemberID.Name == propertyName) {
                return true;
            } else {
                return false;
            }
        }

        #endregion
    }
}
